﻿namespace ClassyBlog.DataAccess
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Linq;

    using DomainObjects;
    using Infrastructure;

    public abstract class ArticleFactory<TArticle> : IArticleFactory<TArticle>
        where TArticle : IArticle
    {
        protected ArticleFactory(IFileSystem fileSystem)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException("fileSystem");
            }

            FileSystem = fileSystem;
        }

        protected IFileSystem FileSystem { get; private set; }

        public TArticle Create(string dataPath, string filePath)
        {
            if (string.IsNullOrWhiteSpace(dataPath))
            {
                throw new ArgumentException(
                    TextMessages.Data_Path_Blank, "dataPath");
            }

            if (string.IsNullOrWhiteSpace(filePath))
            {
                throw new ArgumentException(
                    TextMessages.ArticleFactory_Create_File_Path_Blank,
                    "filePath");
            }

            if (!FileSystem.FileExists(filePath))
            {
                return default(TArticle);
            }

            var name = Path.GetFileNameWithoutExtension(filePath);
            var extension = Path.GetExtension(filePath);

            if (string.IsNullOrWhiteSpace(name) ||
                string.IsNullOrWhiteSpace(extension))
            {
                return default(TArticle);
            }

            if (extension.StartsWith(".", StringComparison.Ordinal))
            {
                extension = extension.Substring(1);
            }

            var separatorIndex = name.IndexOf(DataFileConfiguration.Separator);

            if (separatorIndex <= 0)
            {
                return default(TArticle);
            }

            var timestamp = name.Substring(0, separatorIndex);

            var publishedAt = Clock.UtcNow();

            var foundDate = DataFileConfiguration.DateFormats
                .Any(dateFormat =>
                    TryParseDate(timestamp, dateFormat, out publishedAt));

            if (!foundDate)
            {
                return default(TArticle);
            }

            var slug = name.Substring(separatorIndex + 1);

            var article = CreateLazy(dataPath, filePath);

            article.PublishedAt = publishedAt;
            article.Slug = slug;
            article.Type = extension;

            return article;
        }

        protected abstract TArticle CreateLazy(
            string dataPath, string filePath);

        private static bool TryParseDate(
            string value, string format, out DateTime date)
        {
            const DateTimeStyles Style = DateTimeStyles.AllowWhiteSpaces |
                                         DateTimeStyles.NoCurrentDateDefault;

            var result = DateTime.TryParseExact(
                value, format, Culture.Invariant, Style, out date);

            return result;
        }
    }
}